﻿using jbt.CodeCandy.AsTrEAM.Contracts;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web.Http;

namespace jbt.CodeCandy.AsTrEAM.Client
{
    /// <summary>
    /// Simple access to a stream manager.
    /// </summary>
    public static class StreamManagerClient
    {
        /// <summary>
        /// Access to stream manager using WCF.
        /// </summary>
        public static class WCF
        {
            /// <summary>
            /// Returns the stream with the specified identifier.
            /// </summary>
            /// <param name="streamManagerServiceUrl">
            /// The URL of stream manager service to connect to.
            /// </param>
            /// <param name="id">
            /// Unique identifier of stream to return.
            /// </param>
            /// <returns>
            /// The stream with the specified identifier.
            /// </returns>
            /// <exception cref="ArgumentException">
            /// <paramref name="streamManagerServiceUrl"/> is null or white space.
            /// - or -
            /// <paramref name="id"/> is null or white space.
            /// </exception>
            public static async Task<HttpResponseMessage> Get(string streamManagerServiceUrl, string id)
            {
                if (String.IsNullOrWhiteSpace(streamManagerServiceUrl))
                {
                    throw new ArgumentException("Stream manager service URL is null or white space.", "streamManagerServiceUrl");
                }

                if (String.IsNullOrWhiteSpace(id))
                {
                    throw new ArgumentException("Id is null or white space.", "id");
                }

                using (var streamManager = StreamManagerServiceClient.Factory.ConnectTo(streamManagerServiceUrl))
                {
                    var request = new StreamDownloadRequest
                    {
                        Id = id,
                    };

                    var streamData = await streamManager.DownloadAsync(request);

                    var httpResponse = new HttpResponseMessage();

                    httpResponse.Content = new StreamContent(streamData.Content);

                    httpResponse.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
                    httpResponse.Content.Headers.ContentDisposition.FileName = streamData.Name;
                    httpResponse.Content.Headers.ContentDisposition.ModificationDate = streamData.LastModified;
                    httpResponse.Content.Headers.ContentDisposition.Size = streamData.ContentLength;

                    httpResponse.Content.Headers.ContentLength = streamData.ContentLength;

                    httpResponse.Content.Headers.ContentType = new MediaTypeHeaderValue(streamData.ContentType);

                    httpResponse.Content.Headers.LastModified = streamData.LastModified;

                    return httpResponse;
                }
            }

            /// <summary>
            /// Creates a new stream.
            /// </summary>
            /// <returns>
            /// Result of create.
            /// </returns>
            /// <exception cref="ArgumentException">
            /// <paramref name="streamManagerServiceUrl"/> is null or white space.
            /// </exception>
            /// <exception cref="ArgumentNullException">
            /// <paramref name="request"/> is null.
            /// </exception>
            public static async Task<HttpResponseMessage> Post(string streamManagerServiceUrl, HttpRequestMessage request)
            {
                if (String.IsNullOrWhiteSpace(streamManagerServiceUrl))
                {
                    throw new ArgumentException("Stream manager service URL is null or white space.", "streamManagerServiceUrl");
                }

                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                if (!request.Content.IsMimeMultipartContent())
                {
                    throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
                }

                var uploadFolder = Path.GetTempPath();

                var multipartDataProvider = new MultipartFormDataStreamProvider(uploadFolder);

                try
                {
                    await request.Content.ReadAsMultipartAsync(multipartDataProvider);

                    var uploadTasks = new List<Task>(multipartDataProvider.FileData.Count);

                    using (var streamManager = StreamManagerServiceClient.Factory.ConnectTo(streamManagerServiceUrl))
                    {
                        foreach (var multipartFileInfo in multipartDataProvider.FileData)
                        {
                            var contentType = multipartFileInfo.Headers.ContentType.MediaType;
                            var fileName = multipartFileInfo.Headers.ContentDisposition.FileName;
                            fileName = fileName.Trim('"');

                            var fileInfo = new FileInfo(multipartFileInfo.LocalFileName);

                            var stream = fileInfo.OpenRead();

                            var streamData = new StreamData
                            {
                                Content = stream,
                                ContentType = contentType,
                                LastModified = DateTime.UtcNow,
                                Name = fileName,
                            };

                            var uploadTask = streamManager.UploadAsync(streamData);

                            uploadTasks.Add(uploadTask);
                        }
                    }

                    Task.WaitAll(uploadTasks.ToArray());

                    var failedTasks = uploadTasks.Where(task => task.Status != TaskStatus.RanToCompletion).ToArray();

                    if (failedTasks.Length != 0)
                    {
                        throw new HttpResponseException(HttpStatusCode.InternalServerError);
                    }

                    return request.CreateResponse(HttpStatusCode.OK);
                }
                catch (System.Exception e)
                {
                    return request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
                }
            }
        }
    }
}